#include "GcovManager.hpp"

#include <tinyxml2.h>
#include <sstream>
#include <algorithm>

#include "main.hpp"
#include "ReadXml_15.1.hpp"

/**
 * -------------- class GcovManager --------------
 */
gcov::GcovManager::GcovManager(GcovMajorVersion majorVersion, GcovMinorVersion minorVersion)
    : m_majorVersion(majorVersion), m_minorVersion(minorVersion)
{
    m_version = makeStringVersion(majorVersion, minorVersion);
    LOG("version: %s", m_version.c_str());
}

gcov::GcovManager::~GcovManager()
{
}

bool gcov::GcovManager::isXml(std::string &xmlFilePath)
{
    return xmlFilePath.find(".xml") != std::string::npos;
}

std::string gcov::GcovManager::makeStringVersion(GcovMajorVersion majorVersion, GcovMinorVersion minorVersion) const
{
    return std::to_string(majorVersion) + "." + std::to_string(minorVersion);
}

std::string gcov::GcovManager::getVersion() const
{
    return m_version;
}

std::shared_ptr<wxlsx::ExcelUtInfoList> gcov::GcovManager::readXmlGcov(std::string xmlFilePath, std::string gcovFilePath)
{
    /* check xml file path */
    if (xmlFilePath.empty() || !isXml(xmlFilePath))
    {
        LOG("xmlFilePath is empty!");
        return nullptr;
    }

    /* check gcov file path */
    if (gcovFilePath.empty())
    {
        LOG("xmlFilePath is empty!");
        return std::make_shared<wxlsx::ExcelUtInfoList>();
    }

    std::shared_ptr<wxlsx::ExcelUtInfoList> res = std::make_shared<wxlsx::ExcelUtInfoList>();

    switch (m_majorVersion)
    {
    case GcovMajorVersion::GCOV_VERSION_15: // major version == 15
        switch (m_minorVersion)
        {
        case GcovMinorVersion::GCOV_VERSION_1: // minor version == 1
            m_currentGcov = std::make_shared<Gcov_15_0>();
            res = m_currentGcov->readXmlGcov(xmlFilePath, gcovFilePath);
            break;
        default:
            LOG("sorry, not support gcov version: %s", m_version.c_str());
            break;
        }
        break;
    default:
        LOG("sorry, not support gcov version: %s", m_version.c_str());
        break;
    }

    return res;
}

/**
 * -------------- class _XmlInfo --------------
 */

std::string gcov::_XmlInfo::toString() const
{
    std::ostringstream oss;

    oss << "caseNumber: " << caseNumber << ", fileName: " << fileName << ", defineOutputFileName: " << defineOutputFileName
        << "className: " << className << ", funcName: " << funcName << ", defineOutputFileName: " << defineOutputFileName << ", caseInfo: ";

    caseInfoList.foreach ([&](std::shared_ptr<gcov::_XmlCaseInfo> caseInfo) -> void
                          { oss << "{" << caseInfo->toString() << "}, "; });

    return oss.str();
}

/**
 * -------------- class XmlCaseInfoList --------------
 */

void gcov::XmlCaseInfoList::foreach (std::function<void(std::shared_ptr<_XmlCaseInfo> info)> func) const
{
    std::for_each(begin(), end(), [&](std::shared_ptr<_XmlCaseInfo> info)
                  { func(info); });
}

std::shared_ptr<gcov::_XmlCaseInfo> gcov::XmlCaseInfoList::get(int number) const
{
    if (size() < static_cast<std::size_t>(number))
    {
        return nullptr;
    }

    int i = 0;
    for (auto it = begin(); it != end(); ++it)
    {
        i++;
        if (i == number)
        {
            return *it;
        }
    }

    return nullptr;
}

/**
 * -------------- class _XmlCaseInfo --------------
 */

std::string gcov::_XmlCaseInfo::toString() const
{
    std::ostringstream oss;
    oss << "caseID: " << caseID
        << ", useOnStatus: " << useOnStatus
        << ", expected: " << expected
        << ", actual: " << actual
        << ", time: " << time;

    return oss.str();
}

/**
 * -------------- class XmlMap --------------
 */

void gcov::XmlMap::foreach (std::function<void(std::string, std::shared_ptr<gcov::XmlInfo>)> func) const
{
    std::for_each(begin(), end(), [&](const std::pair<const std::string, std::shared_ptr<gcov::_XmlInfo>> &pair) -> void
                  { func(pair.first, pair.second); });
}